//
// Created by tang on 22-10-24.
//

#include "imgui_ext.hpp"

#include "glm/gtc/type_ptr.hpp"
#include "imgui.h"
#include "imgui_internal.h"

#include "gl/texture.hpp"

namespace ImGui {

    static constexpr const char *labels[] {
        "X",
        "##X",
        "X/R/S Part",
        "Y",
        "##Y",
        "Y/G/T Part",
        "Z",
        "##Z",
        "Z/B/P Part",
        "W",
        "##W",
        "W/A/Q Part",
    };

    static const ImVec4 colors[] {
        // X
        ImVec4{ 0.8f, 0.1f, 0.15f, 1.0f },
        ImVec4{ 0.9f, 0.2f, 0.2f, 1.0f  },
        ImVec4{ 0.8f, 0.1f, 0.15f, 1.0f },

        // Y
        ImVec4{ 0.2f, 0.7f, 0.2f, 1.0f },
        ImVec4{ 0.3f, 0.8f, 0.3f, 1.0f },
        ImVec4{ 0.2f, 0.7f, 0.2f, 1.0f },

        // Z
        ImVec4{ 0.1f, 0.25f, 0.8f, 1.0f },
        ImVec4{ 0.2f, 0.35f, 0.9f, 1.0f },
        ImVec4{ 0.1f, 0.25f, 0.8f, 1.0f },

        // W
        ImVec4{ 0.65f, 0.65f, 0.65f, 1.0f },
        ImVec4{ 0.35f, 0.35f, 0.35f, 1.0f },
        ImVec4{ 0.4f,  0.4f,  0.4f,  1.0f },
    };

    static const char* const types[] {
        "",
        "",
        "Vector 2D",
        "Vector 3D",
        "Vector 4D",
    };

    static void ShowTooltip(size_t dim, const float *values)
    {
        BeginTooltip();
        BulletText("%s", types[dim]);
        ImGui::Separator();
        PushStyleColor(ImGuiCol_Text, ImVec4(1.0,1.0,0.0,1.0));
        ImGui::TextUnformatted("Right click to reset values.");
        PushStyleColor(ImGuiCol_Text, ImVec4(0.0,1.0,1.0,1.0));
        switch (dim) {
            case 2:
                Text("Reset Value: {%.3f,%.3f}", values[0], values[1]);
                break;
            case 3:
                Text("Reset Value: {%.3f,%.3f,%.3f}", values[0], values[1], values[2]);
                break;
            case 4:
                Text("Reset Value: {%.3f,%.3f,%.3f,%.3f}", values[0], values[1], values[2], values[3]);
                break;
            default:
                TextColored(ImVec4(1.0,0.0,0.0,1.0), "Unknown value!!");
                break;
        }
        PopStyleColor(2);
        EndTooltip();
    }

    template <size_t Dim>
    bool DrawVecEditor(const char* label, float* values, const float *minValues, const float *maxValues, const float* resetValues, float speed)
    {
        static_assert(Dim >= 1 && Dim <= 4);
        static const glm::vec4 default_min_max {0.f, 0.f, 0.f, 0.f};

        if (minValues == nullptr)
            minValues = value_ptr(default_min_max);
        if (maxValues == nullptr)
            maxValues = value_ptr(default_min_max);

        using namespace ImGui;

        bool changed = false;

        PushID(label);
        const bool open = TreeNodeEx(label, ImGuiTreeNodeFlags_Framed);
        if (IsItemHovered())
            ShowTooltip(Dim, resetValues);
//        if (IsItemHovered() && IsMouseClicked(ImGuiMouseButton_Right))
//        {
//            for (int i=0; i<Dim; ++i) {
//                values[i] = resetValues[i];
//            }
//        }
        if (BeginPopupContextItem("##Menus", ImGuiPopupFlags_MouseButtonRight))
        {
            if (Selectable("Set to <min>")) {
                for (int i=0; i<Dim; ++i) {
                    values[i] = minValues[i];
                }
            }
            if (Selectable("Set to <max>")) {
                for (int i=0; i<Dim; ++i) {
                    values[i] = maxValues[i];
                }
            }
            if (Selectable("Reset values")) {
                for (int i=0; i<Dim; ++i) {
                    values[i] = resetValues[i];
                }
            }
            EndPopup();
        }
        if (open)
        {
            AlignTextToFramePadding();
            for (size_t i=0; i<Dim; ++i)
            {
                PushStyleColor(ImGuiCol_Button, colors[3*i]);
                PushStyleColor(ImGuiCol_ButtonHovered, colors[3*i+1]);
                PushStyleColor(ImGuiCol_ButtonActive, colors[3*i+2]);
                if (Button(labels[3*i])) {
                    values[i] = resetValues[i];
                    changed = true;
                }
                PopStyleColor(3);
                if (IsItemHovered())
                    SetTooltip("Reset to %.3f", resetValues[i]);
                SameLine();
                SetNextItemWidth(GetFontSize() * 8);
                changed |= DragFloat(labels[3*i+1], values+i, speed, minValues[i], maxValues[i], "%.3f", 0);
                if (IsItemHovered())
                    SetTooltip("%s", labels[3*i+2]);
            }
            TreePop();
        }
        PopID();
        return changed;
    }

    bool Vector4Editor(const char *name, glm::vec4 &vec4, const glm::vec4 &reset, float speed) {
        return DrawVecEditor<4>(name, value_ptr(vec4), nullptr, nullptr, value_ptr(reset), speed);
    }

    bool Vector3Editor(const char *name, glm::vec3 &vec3, const glm::vec3 &reset, float speed) {
        return DrawVecEditor<3>(name, value_ptr(vec3), nullptr, nullptr, value_ptr(reset), speed);
    }

    bool Vector2Editor(const char *name, glm::vec2 &vec2, const glm::vec2 &reset, float speed) {
        return DrawVecEditor<2>(name, value_ptr(vec2), nullptr, nullptr, value_ptr(reset), speed);
    }

    bool Vector4Ranged(const char *name, glm::vec4 &vec4, const glm::vec4 &min, const glm::vec4 &max, const glm::vec4 &reset, float speed) {
        return DrawVecEditor<4>(name, value_ptr(vec4), value_ptr(min), value_ptr(max), value_ptr(reset), speed);
    }

    bool Vector3Ranged(const char *name, glm::vec3 &vec3, const glm::vec3 &min, const glm::vec3 &max, const glm::vec3 &reset, float speed) {
        return DrawVecEditor<4>(name, value_ptr(vec3), value_ptr(min), value_ptr(max), value_ptr(reset), speed);
    }

    bool Vector2Ranged(const char *name, glm::vec2 &vec2, const glm::vec2 &min, const glm::vec2 &max, const glm::vec2 &reset, float speed) {
        return DrawVecEditor<4>(name, value_ptr(vec2), value_ptr(min), value_ptr(max), value_ptr(reset), speed);
    }

    void Image(const GL::Texture &tex, ImVec2 size)
    {
        if (size.x == 0. || size.y == 0.)
        {
            auto const texSize = tex.Size();
            Image((ImTextureID)tex.TexID(), ImVec2{texSize.x, texSize.y});
        }
        else
        {
            Image((ImTextureID)tex.TexID(), size);
        }
    }

}
